/**
 * 文件添加接口测试用例
 *
 * Created by snowingsea on 2020/02/24.
 */
const fs = require('fs-extra');
const chai = require('chai');
const should = chai.should(); // eslint-disable-line no-unused-vars
chai.use(require('chai-http'));
const deepcopy = require('deepcopy');
const path = require('path');
const randomstring = require('randomstring');

const common = require('./../../common');
const Administrator = require('./../../../models/administrator');
const File = require('./../../../models/file');
const uri = require('./../uri');
const config = require('./../../api-config');

const files = require('./../../../../phoenix/models/files');
const utility = require('./../../../../phoenix/models/utility');

module.exports = describe('/POST files/:id/file.json', function () {
  const administrator = new Administrator();
  let rootDirectory;
  const newUserIds = [];
  function url(fileId) {
    if (!fileId) {
      fileId = rootDirectory.fileId;
    }
    return `/files/${fileId}/file.json`;
  }

  before(function (done) {
    administrator.login()
      .then(() => administrator.createRandomTester())
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((project) => project.createRandomVersion())
      .then((res) => {
        rootDirectory = res.rootDirectory;
        done();
      })
      .catch(done);
  });
  after(function (done) {
    administrator.removeUsers(newUserIds)
      .then(() => {
        done();
      })
      .catch(done);
  });

  common.should.be.user(uri, url, 'post');
  common.should.be.tester(uri, url, 'post');

  // 正常添加空文件/文件夹
  [files.kind.file, files.kind.directory].forEach((kind, index) => { // 文件类型
    it(`it should ${index === 0 ? 'ADD' : 'add'} a empty ${kind === files.kind.file ? 'file' : 'directory'} successfully`, function (done) {
      const fileName = randomstring.generate(16);
      rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          chai.request(uri)
            .post(url())
            .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
            .query({ fileName, kind })
            .end((err, res) => {
              res.should.have.status(200);
              common.should.be.fileMessage(res.body);
              res.body.fileName.should.be.eql(fileName);
              res.body.path.should.be.eql(`./${fileName}`);
              res.body.status.should.be.eql(files.statusKind.notCompiled);
              res.body.kind.should.be.eql(kind);
              res.body.should.not.have.property('failed');
              done();
            });
        })
        .catch(done);
    });
  });

  // 上传文件新建文件/文件夹
  // 文件类型
  [files.kind.file, files.kind.directory].forEach((kind) => {
    const packageTypes = kind === files.kind.directory ? ['zip'] : [''];
    // 压缩包类型
    packageTypes.forEach((packageType) => {
      // 新建文件/文件夹名
      [
        undefined,
        randomstring.generate(8),
        `${randomstring.generate(8)}/${randomstring.generate(8)}/${randomstring.generate(8)}`,
      ].forEach((fileName) => {
        const fileNameString = fileName ? `"${fileName}" ` : '';
        const kindNameString = kind === files.kind.file ? 'file' : 'directory';
        const packageTypeString = kind === files.kind.file ? '' : ` of ${packageType}`;

        it(`it should add a ${fileNameString}${kindNameString}${packageTypeString} with upload file successfully`, function (done) {
          const homeDir = path.join(config.path.cacheDir, randomstring.generate(16));
          const uploadPath = kind === files.kind.file ? homeDir : `${homeDir}.${packageType}`;
          const content = randomstring.generate(100);
          fs.ensureFile(uploadPath)
            .then(() => {
              if (kind === files.kind.file) {
                return utility.writeFile(content, uploadPath);
              }
              return File.createRandomPackage(packageType, homeDir, uploadPath);
            })
            .then(() => rootDirectory.projectVersion.project.creator.login())
            .then(() => {
              chai.request(uri)
                .post(url())
                .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
                .query({ fileName, kind })
                .attach('file', fs.readFileSync(uploadPath), path.basename(uploadPath))
                .end((err, res) => {
                  res.should.have.status(200);
                  common.should.be.fileMessage(res.body);
                  res.body.kind.should.be.eql(kind);
                  if (!fileName) {
                    if (kind === files.kind.file) {
                      fileName = path.basename(uploadPath);
                    } else {
                      fileName = path.basename(uploadPath, path.extname(uploadPath));
                    }
                  }
                  res.body.fileName.should.be.eql(path.basename(fileName));

                  const file = new File(rootDirectory.projectVersion, res.body.fileId);
                  file.getTree()
                    .then((res) => {
                      common.should.be.fileTreeMessage(res);
                      if (kind === files.kind.file) {
                        return Promise.resolve();
                      }
                      res.tree.should.have.property('children');
                      res.tree.children.should.be.an('array');
                      path.normalize(res.tree.path).should.be.eql(path.normalize(fileName));
                      res.tree.fileName.should.be.eql(path.basename(fileName));
                      res.tree.children = res.tree.children.filter((child) => {
                        const { kind } = child;
                        return kind === files.kind.directory;
                      });
                      if (res.tree.children.length === 0) {
                        return Promise.resolve();
                      }
                      res.tree.children.length.should.be.eql(1);
                      res.tree.children[0].fileName.should.be.eql(path.basename(homeDir));
                      return common.should.complete.directory(res.tree.children[0], homeDir);
                    })
                    .then(() => {
                      done();
                    })
                    .catch(done);
                });
            })
            .catch(done);
        });
      });
    });
  });

  // 缺少参数
  const query = {
    kind: files.kind.file,
  };
  Object.keys(query).forEach((key) => {
    it(`it should deny invalid request of missing "${key}"`, function (done) {
      const invalidQuery = deepcopy(query);
      delete invalidQuery[key];
      rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          chai.request(uri)
            .post(url())
            .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
            .query(invalidQuery)
            .end((err, res) => {
              res.should.have.status(400);
              common.should.be.errorMessage(res.body);
              done();
            });
        })
        .catch(done);
    });
  });

  // 不可向冻结项目添加新文件
  it('it should fail to add new file to frozen project', function (done) {
    let newProjectId;
    let newRootDirectoryId;
    rootDirectory.projectVersion.project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then(({ rootDirectory }) => {
        newRootDirectoryId = rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.freezeProject(newProjectId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newRootDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ kind: files.kind.file, fileName: randomstring.generate(8) })
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向冻结项目版本添加新文件
  it('it should fail to add new file to frozen project version', function (done) {
    let newRootDirectoryId;
    rootDirectory.projectVersion.project.createRandomVersion()
      .then(({ versionId, rootDirectory }) => {
        newRootDirectoryId = rootDirectory.fileId;
        return rootDirectory.projectVersion.project.freezeVersion(versionId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newRootDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ kind: files.kind.file, fileName: randomstring.generate(8) })
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向他人项目添加文件
  it('it should fail to add file when the project is not yours', function (done) {
    let otherDirectoryId;
    administrator.createRandomTester()
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((project) => project.createRandomVersion())
      .then((res) => {
        otherDirectoryId = res.rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .post(url(otherDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ fileName: randomstring.generate(8), kind: files.kind.file })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向不存在的项目添加文件
  it('it should fail to add file when the project is not exist', function (done) {
    let newProjectId;
    let newRootDirectoryId;
    rootDirectory.projectVersion.project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then((version) => {
        newRootDirectoryId = version.rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.removeProject(newProjectId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newRootDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ fileName: randomstring.generate(8), kind: files.kind.file })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向不存在的文件添加文件
  it('it should fail to add file when the directory is not exist', function (done) {
    let newDirectoryId;
    rootDirectory.createEmptyFile(files.kind.directory)
      .then((file) => {
        newDirectoryId = file.fileId;
        return rootDirectory.removeFile(newDirectoryId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ fileName: randomstring.generate(8), kind: files.kind.file })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向文件添加新文件
  it('it should fail to add a file to file', function (done) {
    let newFileId;
    rootDirectory.createEmptyFile()
      .then(({ fileId }) => {
        newFileId = fileId;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .post(url(newFileId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ fileName: randomstring.generate(8), kind: files.kind.file })
          .end((err, res) => {
            res.should.have.status(416);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可添加一个已存在的文件
  it('it should fail to add a file when the file path exist', function (done) {
    let filePath;
    rootDirectory.createEmptyFile()
      .then(({ path }) => {
        filePath = path;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .post(url())
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ fileName: filePath, kind: files.kind.file })
          .end((err, res) => {
            res.should.have.status(409);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });
});
